/** @noSelfInFile */
import mitt, {Handler} from '@//lib/mitt'
import Cache from "@/tool/Cache";
import TriggerUtil from "@/util/system/TriggerUtil";
import UnitSpellEvent from "@/tool/event/UnitSpellEvent";
import UnitDeathEvent from "@/tool/event/UnitDeathEvent";
import UnitAttackedEvent from "@/tool/event/UnitAttackedEvent";
import UnitDamagedEvent from "../tool/event/UnitDamagedEvent";
import UnitEvent from "../tool/event/UnitEvent";
import UnitItemEvent from "../tool/event/UnitItemEvent";
import SingletonUtil from "@/SingletonUtil";

const emitter = mitt()
const cache = new Cache();

/**
 * 太阳事件(总线)
 * 所有监听的事件回调公用一个触发器去注册
 */
export default class SolarEvent {


    constructor() {
        if (SingletonUtil.notFirstTime(SolarEvent)) {
            print("不能重复new SolarEvent() 请直接使用全局变量se进行访问")
            return;
        }
    }


    /**
     * 注册一个事件接收器
     * @param type
     * @param handler
     */
    on<Key extends keyof AppEventType>(type: Key, handler: (data: AppEventType[Key]) => void) {
        emitter.on(type as any, handler as any)
    }

    /**
     * 发布一个事件
     * @param type
     * @param data
     */
    emit<Key extends keyof AppEventType>(type: Key, data?: AppEventType[Key]) {
        emitter.emit(type as any, data as any)
    }


    /**
     *
     * @param typeKey 注册时所使用的key 比如 onUnitDamaged注册的事件 key为onUnitDamaged
     * @param seHandler 为注册时返回的值(回调函数)
     */
    off<Key extends keyof AppEventType>(typeKey: string, seHandler: Handler): void {
        emitter.off(typeKey, seHandler)
    }


    /**
     * 玩家输入聊天信息
     */
    playerChat(chatMessageToDetect: string, callback: (triggerPlayer: player, chatString: string) => void, exactMatchOnly: boolean = true) {
        const key = "playerChat:" + chatMessageToDetect + ":" + exactMatchOnly;
        cache.get(key, () => {
            let trigger = CreateTrigger()
            for (let i = 0; i < bj_MAX_PLAYER_SLOTS; i++) {
                TriggerRegisterPlayerChatEvent(trigger, Player(i), chatMessageToDetect, exactMatchOnly);
            }
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            callback(GetTriggerPlayer(), GetEventPlayerChatString())
        })
    }

    /**
     * 任意单位伤害事件
     * @deprecated see onUnitDamaged
     */
    unitDamaged(callback: (triggerUnit: unit, damageSourceUnit: unit, damage: number) => void, onlyHasDamageSource: boolean = true) {
        const key = "unitDamaged";
        cache.get(key, () => {
            let trigger = CreateTrigger()
            TriggerUtil.SystemAnyUnitDamagedRegistTrigger(trigger)
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            if (onlyHasDamageSource && !IsHandle(GetEventDamageSource())) {
                return
            }
            callback(GetTriggerUnit(), GetEventDamageSource(), GetEventDamage())
        })
    }

    /**
     *
     * @param callback
     * @param onlyHasDamageSource 默认会判断是否有伤害来源 (投射物射出后 单位死亡 然后投射物造成伤害可能会导致造成伤害却没有伤害来源单位)
     */
    onUnitDamaged(callback: (e: UnitDamagedEvent) => void, onlyHasDamageSource: boolean = true): Handler {
        const key = "onUnitDamaged";
        cache.get(key, () => {
            let trigger = CreateTrigger()
            TriggerUtil.SystemAnyUnitDamagedRegistTrigger(trigger)
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        });
        let seh = () => {
            if (onlyHasDamageSource && !IsHandle(GetEventDamageSource())) {
                return
            }
            callback(UnitDamagedEvent.instance)
        }
        emitter.on(key, seh)
        return seh;
    }

    /**
     * 单位进入区域
     */
    onEnterRect(whichRect: rect = bj_mapInitialPlayableArea, callback: (e: UnitEvent) => void) {
        const key = "onEnterRect:" + GetHandleId(whichRect);
        cache.get(key, () => {
            let rectRegion: region = CreateRegion()
            RegionAddRect(rectRegion, whichRect)
            //任意单位死亡事件
            let trigger = CreateTrigger()
            TriggerRegisterEnterRegion(trigger, rectRegion, null);
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 当任意单位进入可用地图区域
     * @param callback
     */
    onUnitEnterMapRect(callback: (e: UnitEvent) => void) {
        const key = "onUnitEnterMapRect";
        cache.get(key, () => {
            let rectRegion: region = CreateRegion()
            RegionAddRect(rectRegion, GetPlayableMapRect())
            //任意单位死亡事件
            let trigger = CreateTrigger()
            TriggerRegisterEnterRegion(trigger, rectRegion, null);
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 获得物品
     * @deprecated see onUnitPickupItem
     * @param callback
     */
    unitPickupItem(callback: (triggerUnit: unit, item: item, itemIdStr: string) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, triggerUnit => {
            callback(triggerUnit, GetManipulatedItem(), id2string(GetItemTypeId(GetManipulatedItem())))
        })
    }

    /**
     * 任意单位事件 - 出售物品
     * @param callback
     */
    onUnitSellItem(callback: (e: UnitItemEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SELL_ITEM, () => {
            callback(UnitItemEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 获得物品
     * @param callback
     */
    onUnitPickupItem(callback: (e: UnitItemEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, () => {
            callback(UnitItemEvent.instance)
        })
    }


    /**
     * 任意单位事件 - 丢弃物品
     * @deprecated see onUnitDropItem
     * @param callback
     */
    unitDropItem(callback: (triggerUnit: unit, item: item, itemIdStr: string) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, triggerUnit => {
            callback(triggerUnit, GetManipulatedItem(), id2string(GetItemTypeId(GetManipulatedItem())))
        })
    }

    /**
     * 任意单位事件 - 丢弃物品
     * @param callback
     */
    onUnitDropItem(callback: (e: UnitItemEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, () => {
            callback(UnitItemEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 使用物品
     * @param callback
     */
    unitUseItem(callback: (triggerUnit: unit, item: item, itemIdStr: string) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_USE_ITEM, triggerUnit => {
            callback(triggerUnit, GetManipulatedItem(), id2string(GetItemTypeId(GetManipulatedItem())))
        })
    }

    /**
     * 任意单位事件 - 使用物品
     * @param callback
     */
    onUnitUseItem(callback: (e: UnitItemEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_USE_ITEM, () => {
            callback(UnitItemEvent.instance)
        })
    }

    // /**
    //  * 玩家单位事件 - 完成建造
    //  * @param callback
    //  */
    // unitConstructFinish(callback: (triggerUnit: unit) => void) {
    //     this.anyUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, triggerUnit => {
    //         callback(triggerUnit)
    //     })
    // }

    /**
     * 玩家单位事件 - 开始建造
     * @param callback
     */
    onUnitConstructStart(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_START, triggerUnit => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 玩家单位事件 - 完成建造
     * @param callback
     */
    onUnitConstructFinish(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, triggerUnit => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 玩家单位事件 - 完成建筑升级
     * @param callback
     */
    onUnitUpgradeFinish(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_FINISH, triggerUnit => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意玩家单位事件 - 选择单位
     * @param callback
     */
    onUnitSelected(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SELECTED, () => {
            callback(UnitEvent.instance)
        })
    }


    private static _sl_last_select_info: {
        [pid: number]: {
            unit: unit
            time: number
        }
    } = {}

    /**
     * 任意玩家双击选择单位事件
     * @param callback
     */
    onUnitDoubleClickSelected(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SELECTED, () => {
            let tu = GetTriggerUnit()
            let playerId = GetPlayerId(GetOwningPlayer(tu));
            let lastSelectInfo = SolarEvent._sl_last_select_info[playerId];
            if (lastSelectInfo == null) {
                SolarEvent._sl_last_select_info[playerId] = {unit: tu, time: time}
                return
            }
            if (lastSelectInfo.unit == tu && (time - lastSelectInfo.time) < 1000) {
                callback(UnitEvent.instance)
            }
            lastSelectInfo.unit = tu;
            lastSelectInfo.time = time;
        })
    }

    /**
     * 任意单位事件 - 开始施放技能
     * @param callback
     */
    unitSpellCast(callback: (triggerUnit: unit, spellAbilityIdStr: string) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, triggerUnit => {
            callback(triggerUnit, id2string(GetSpellAbilityId()))
        })
    }

    /**
     * 任意单位事件 - 开始施放技能
     * @param callback
     * @param abilityId
     */
    onUnitSpellChannel(callback: (event: UnitSpellEvent) => void, abilityId?: string) {
        this.anyUnitSpellEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, callback, abilityId);
    }

    /**
     * 任意单位事件 - 准备施放技能
     * @param callback
     * @param abilityId
     */
    onUnitSpellCast(callback: (event: UnitSpellEvent) => void, abilityId?: string) {
        this.anyUnitSpellEvent(EVENT_PLAYER_UNIT_SPELL_CAST, callback, abilityId);
    }

    /**
     * 任意单位事件 - 发动技能效果
     * @param callback
     */
    unitSpellEffect(callback: (triggerUnit: unit, spellAbilityIdStr: string) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, triggerUnit => {
            callback(triggerUnit, id2string(GetSpellAbilityId()))
        })
    }

    /**
     * 任意单位事件 - 发动技能效果
     * @param callback
     * @param abilityId
     */
    onUnitSpellEffect(callback: (event: UnitSpellEvent) => void, abilityId?: string) {
        this.anyUnitSpellEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, callback, abilityId);
    }

    /**
     * 任意单位事件 - 死亡
     * @param callback
     */
    unitDeath(callback: (triggerUnit: unit, killingUnit: unit) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_DEATH, triggerUnit => {
            callback(triggerUnit, GetKillingUnit())
        })
    }

    /**
     * 任意单位事件 - 死亡
     * @param callback
     */
    onUnitDeath(callback: (e: UnitDeathEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_DEATH, () => {
            callback(UnitDeathEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 开始研究科技
     * @param callback
     */
    onUnitResearchStart(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_RESEARCH_START, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 研究科技完成
     * @param callback
     */
    onUnitResearchFinish(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_RESEARCH_FINISH, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 被攻击
     * @param callback
     */
    unitAttacked(callback: (triggerUnit: unit, attacker: unit) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, triggerUnit => {
            callback(triggerUnit, GetAttacker())
        })
    }

    /**
     * 任意单位事件 - 被攻击
     * @param callback
     */
    onUnitAttacked(callback: (e: UnitAttackedEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, () => {
            callback(UnitAttackedEvent.instance)
        })
    }

    /**
     * 任意单位事件 - 出售单位
     * @param callback
     */
    onUnitSellUnit(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SELL, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意单位 受到攻击伤害
     * 这个可以防止单位寸止 即抬手时就触发了被攻击事件
     * @param callback
     */
    onUnitAttackedDamage(callback: (e: UnitDamagedEvent) => void) {
        this.onUnitDamaged((e) => {
            if (0 != EXGetEventDamageData(EVENT_DAMAGE_DATA_IS_ATTACK)) {
                callback(e);
            }
        })
    }

    /**
     * 英雄单位 - 升级
     * @param callback
     */
    onHeroLevelUp(callback: (e: UnitEvent) => void) {
        this.anyUnitEvent(EVENT_PLAYER_HERO_LEVEL, () => {
            callback(UnitEvent.instance)
        })
    }

    /**
     * 任意单位事件
     */
    anyUnitEvent(whichPlayerUnitEvent: playerunitevent, callback: (triggerUnit: unit) => void) {

        const key = "anyUnitEvent:" + GetHandleId(whichPlayerUnitEvent);
        cache.get(key, () => {
            //任意单位攻击事件
            let trigger = CreateTrigger()
            for (let i = 0; i < bj_MAX_PLAYER_SLOTS; i++) {
                TriggerRegisterPlayerUnitEvent(trigger, Player(i), whichPlayerUnitEvent, null)
            }
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            callback(GetTriggerUnit())
        })
    }

    /**
     * 任意单位释放技能总事件
     */
    anyUnitSpellEvent(whichPlayerUnitEvent: playerunitevent, callback: (spellEvent: UnitSpellEvent) => void, spellAbilityId?: string) {
        const key = "anyUnitSpellEvent:" + GetHandleId(whichPlayerUnitEvent);
        cache.get(key, () => {
            //任意单位攻击事件
            let trigger = CreateTrigger()
            for (let i = 0; i < bj_MAX_PLAYER_SLOTS; i++) {
                TriggerRegisterPlayerUnitEvent(trigger, Player(i), whichPlayerUnitEvent, null)
            }
            TriggerAddAction(trigger, () => {
                emitter.emit(key);
                let regCallback: (spellEvent: UnitSpellEvent) => void = cache.get(key + ":" + id2string(GetSpellAbilityId()));
                if (regCallback) {
                    regCallback(UnitSpellEvent.instance)
                }
            })
            return true;
        })
        if (spellAbilityId) {
            cache.put(key + ":" + spellAbilityId, callback);
        } else {
            emitter.on(key, () => {
                callback(UnitSpellEvent.instance)
            })
        }

    }


    /**
     * 清除所有注册的事件
     */
    clear() {
        cache.clear();
        emitter.all.clear();
    }


    /**
     * 弃用的函数
     */
    /**
     * 任意玩家单位事件 - 选择单位
     * @param callback
     * @deprecated
     */
    unitSelected(callback: (triggerUnit: unit, triggerPlayer: player) => void) {
        this.anyUnitEvent(EVENT_PLAYER_UNIT_SELECTED, triggerUnit => {
            callback(triggerUnit, GetTriggerPlayer())
        })
    }

    /**
     * 单位进入区域
     * @deprecated
     */
    enterRect(whichRect: rect = bj_mapInitialPlayableArea, callback: (triggerUnit: unit) => void) {

        const key = "enterRect:" + GetHandleId(whichRect);
        cache.get(key, () => {
            let rectRegion: region = CreateRegion()
            RegionAddRect(rectRegion, whichRect)
            //任意单位死亡事件
            let trigger = CreateTrigger()
            TriggerRegisterEnterRegion(trigger, rectRegion, null);
            TriggerAddAction(trigger, () => {
                emitter.emit(key)
            })
            return true;
        })
        emitter.on(key, () => {
            callback(GetTriggerUnit())
        })
    }
}
/**
 * se : SolarEvent (太阳事件总线)
 */
declare global {
    let se: SolarEvent;
}